出处:掘金

原作者:ErpanOmer


跨源资源共享(CORS)的预检请求(Preflight Request)机制中,TTL 缓存机制的核心作用是减少不必要的 OPTIONS 请求,从而提高性能、降低服务器压力

CORS 和预检请求回顾

在现代 Web 应用中,出于安全原因,浏览器会对跨源请求(Cross-Origin Requests) 实施限制。为解决合法跨域通信需求,W3C 提出了 CORS(Cross-Origin Resource Sharing)规范

什么是预检请求?

某些跨源请求会被认为是“有风险的请求”,例如:

这类请求在正式发送之前,浏览器会自动先发送一个 OPTIONS 请求,即“预检请求(Preflight Request)”,以确认服务器是否允许这个跨域行为

同源策略与跨域

预检请求的性能瓶颈

虽然预检是为了安全,但频繁的预检请求带来了显著的性能问题:

举例说明:

如果每次请求都进行一次预检,效率极低

TTL 缓存机制

为缓解上述问题,CORS 引入了 Access-Control-Max-Age 响应头,允许浏览器在一段时间内缓存预检请求的结果,从而跳过后续的 OPTIONS 请求

响应头定义:

Access-Control-Max-Age: 86400

表示该响应的预检结果可以被缓存 86400 秒(即 24 小时)

TTL 缓存机制的工作原理

下面我们从浏览器实现角度,详细讲解 TTL 缓存如何影响预检逻辑

缓存命中规则

浏览器会以以下维度缓存预检请求的结果:

缓存记录会绑定在这些组合上。例如: 如果你请求 https://api.example.com/data,用 PUT 方法加了一个 X-Token 头,预检结果会单独为此组合缓存

缓存位置:浏览器内部内存或磁盘缓存

缓存一般存储于浏览器的内存中,或者在某些情况下写入磁盘(开发者模式下通常禁用磁盘缓存)。当 TTL 时间到达后,缓存会被销毁

浏览器行为逻辑示意

示例:如何设置 TTL 缓存

后端示例(Node.js + Express):

app.options('/api/data', (req, res) => {
  res.set({
    'Access-Control-Allow-Origin': 'https://example.com',
    'Access-Control-Allow-Methods': 'GET, POST, PUT',
    'Access-Control-Allow-Headers': 'Content-Type, X-Token',
    'Access-Control-Max-Age': '86400' // 缓存一天
  })
  res.sendStatus(200)
})

后续 24 小时内,只要是相同条件的请求,不再触发 OPTIONS 请求

注意事项与误区

缓存时间

Access-Control-Max-Age 是服务器端的承诺,不是浏览器的强制缓存周期

部分浏览器会设定最大上限,即使你写了几天的时间,也可能被强制缩短。例如:

请求变化会导致缓存失效

哪怕是 header 多了一个字段,method 变了,URL 路径变了,都会重新触发预检

开发环境中的干扰

Chrome 的“Disable cache”开启时,预检缓存会被禁用。很多人误以为设置失败,其实是开发者工具影响了行为

性能提升效果

对高频接口(如每次点击都会调用的搜索接口),加上合理的 TTL,性能提升显著:

最佳实践总结

项目 建议
设置缓存 建议 Access-Control-Max-Age: 600 或更长
控制 header 尽量精简自定义 header,减少触发预检概率
method 选择 使用 GETPOSTContent-Typeapplication/x-www-form-urlencoded
缓存命中优化 保持接口请求一致性(路径、方法、headers)
环境检测 测试时注意关闭“Disable cache”选项

结语

Access-Control-Max-Age 并不是什么神秘参数,而是浏览器为优化跨域请求性能所设计的 TTL 缓存机制。通过合理利用该机制,可以大幅减少不必要的 OPTIONS 请求,从而提升前端性能,优化用户体验